
#include "Entities/BaseEntity.h"
#include "Entities/ComponentTypeEnum.h"
#include "Base.h"
#include "Game/Game.h"
#include "Sprite/SpriteRenderer.h"
#include "Sprite/SpriteTypes.h"
#include "Assets/AssetManager.h"
#include "Maths/Matrix3.h"
#include "Maths/Matrix4.h"
#include "PhysicComponent.h"
#include "Resources/ResourceManager.h"
#include "Graphics/Color.h"
#include "EventHandler/EventHandler.h"
#include "StringUtils.h"


#include <string>

#include <Memory/DebugNew.h>

namespace CasaEngine
{

/**
 * 
 */
PhysicComponent::PhysicComponent(BaseEntity* pEntity_)
	: Component(pEntity_, PHYSIC2D)
{
	m_fRadius = 1.0f;
}

/**
 * 
 */
PhysicComponent::~PhysicComponent()
{

}


/**
 * 
 */
void PhysicComponent::Initialize()
{
	m_pTransform = GetEntity()->GetComponentMgr()->GetComponent<Transform2DComponent>();
	CASAENGINE_ASSERT(m_pTransform != NULL && "PhysicComponent::Initialize() can't find the Transform2DComponent. Please add it before add a PhysicComponent");
}

/**
 * 
 */
void PhysicComponent::Update(const GameTime& /*gameTime_*/)
{
	
}

/**
 * 
 */
/*void PhysicComponent::HandleEvent(const Event* pEvent_)
{

}*/

void PhysicComponent::Initialize(float radius, Vector2F velocity, float max_speed, 
	Vector2F heading, float mass, float turn_rate, float max_force )
{
	m_fRadius = radius;
	m_vHeading = heading;
	m_vVelocity = velocity;
	m_fMass = mass;
	m_vSide = m_vHeading.Perp();
	m_fMaxSpeed = max_speed;
	m_fMaxTurnRate = turn_rate;
	m_fMaxForce = max_force;
}

/**
 * 
 */
float PhysicComponent::GetRadius() const 
{ 
	return m_fRadius; 
}

/**
 * 
 */
void PhysicComponent::SetRadius(float val_) 
{ 
	m_fRadius = val_; 
}

Vector2F  PhysicComponent::Velocity()const{return m_vVelocity;}
void      PhysicComponent::SetVelocity(const Vector2F& NewVel){m_vVelocity = NewVel;}

float     PhysicComponent::Mass()const{return m_fMass;}

Vector2F  PhysicComponent::Side()const{return m_vSide;}

float     PhysicComponent::MaxSpeed()const{return m_fMaxSpeed;}                       
void      PhysicComponent::SetMaxSpeed(float new_speed){m_fMaxSpeed = new_speed;}

float     PhysicComponent::MaxForce()const{return m_fMaxForce;}
void      PhysicComponent::SetMaxForce(float mf){m_fMaxForce = mf;}

bool      PhysicComponent::IsSpeedMaxedOut()const{return m_fMaxSpeed*m_fMaxSpeed >= m_vVelocity.LengthSq();}
float     PhysicComponent::Speed() const{return m_vVelocity.Length();}
float     PhysicComponent::SpeedSq() const{return m_vVelocity.LengthSq();}

Vector2F  PhysicComponent::Heading() const{return m_vHeading;}

float     PhysicComponent::MaxTurnRate() const{return m_fMaxTurnRate;}
void      PhysicComponent::SetMaxTurnRate(float val){m_fMaxTurnRate = val;}


//--------------------------- RotateHeadingToFacePosition ---------------------
//
//  given a target position, this method rotates the entity's heading and
//  side vectors by an amount not greater than m_dMaxTurnRate until it
//  directly faces the target.
//
//  returns true when the heading is facing in the desired direction
//-----------------------------------------------------------------------------
inline bool PhysicComponent::RotateHeadingToFacePosition(Vector2F target)
{
	Vector2F toTarget = target - m_pTransform->GetPosition();
	toTarget.Normalize();

	//first determine the angle between the heading vector and the target
	float angle = acos(m_vHeading.Dot(toTarget));

	//return true if the player is facing the target
	if (angle < 0.00001f) return true;

	//clamp the amount to turn to the max turn rate
	if (angle > m_fMaxTurnRate) angle = m_fMaxTurnRate;

	//The next few lines use a rotation matrix to rotate the player's heading
	//vector accordingly
	Matrix3 RotationMatrix;

	//notice how the direction of rotation has to be determined when creating
	//the rotation matrix
	RotationMatrix.Rotate(angle * m_vHeading.Sign(toTarget));	
	RotationMatrix.TransformVector2F(m_vHeading);
	RotationMatrix.TransformVector2F(m_vVelocity);

	//finally recreate m_vSide
	m_vSide = m_vHeading.Perp();

	return false;
}


//------------------------- SetHeading ----------------------------------------
//
//  first checks that the given heading is not a vector of zero length. If the
//  new heading is valid this fumction sets the entity's heading and side 
//  vectors accordingly
//-----------------------------------------------------------------------------
inline void PhysicComponent::SetHeading(Vector2F new_heading)
{
	CASAENGINE_ASSERT( (new_heading.LengthSq() - 1.0) < 0.00001);

	m_vHeading = new_heading;

	//the side vector must always be perpendicular to the heading
	m_vSide = m_vHeading.Perp();
}


/**
 * 
 */
void PhysicComponent::Write(std::ostream& /*os*/) const
{

}

/**
 * 
 */
void PhysicComponent::Read (std::ifstream& /*is*/)
{

}

} // namespace CasaEngine
